package com.feifan.mybatis;

import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.util.Properties;

/**
 * @author Donald
 * @create 2019-04-22 22:43
 * sql拦截器设置、、源码类（plugin）
 * Mybatis 拦截器 允许使用的插件来拦截的方法包括 Executor (update, query, flushStatements, commit, rollback, getTransaction, close,
 *  * isClosed) ParameterHandler (getParameterObject, setParameters)
 *  * ResultSetHandler(handleResultSets,handleOutputParameters) StatementHandler (prepare, parameterize, batch, update,
 *  * query)
 * @Intercepts用于表明当前的对象是一个Interceptor
 * @Signature则表明要拦截的接口、方法以及对应的参数类型 Mybatis 拦截器 允许使用的插件来拦截的方法包括 Executor (update, query, flushStatements, commit, rollback, getTransaction, close,
 * * isClosed) ParameterHandler (getParameterObject, setParameters)
 * * ResultSetHandler(handleResultSets,handleOutputParameters) StatementHandler (prepare, parameterize, batch, update,
 * * query)
 */

@Component
@Configuration
@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
        ,@Signature(type = Executor.class, method = "update",args = {MappedStatement.class, Object.class})
        ,@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class,
                CacheKey.class, BoundSql.class})//拦截INSERT
        //@Signature(method = "prepare", type = StatementHandler.class, args = { Connection.class })
            })
public class MySqlInterceptor implements Interceptor {

    private static final Logger LOGGER = LoggerFactory.getLogger(MySqlInterceptor.class);

    /**
     * intercept方法就是要进行拦截的时候要执行的方法
     *
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        /**
         * args[0] mappedstatment
         * args[1] 参数
         * args[2] ?
         * 目标方法执行 增强拦截器,记录sql的执行时间
         */
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        String mapperMethod = ms.getId();//执行语句，mapper.xml id
        SqlSource sqlSource = (SqlSource) ms.getSqlSource();
        BoundSql sql = sqlSource.getBoundSql(args[1]);
        LOGGER.info("mybatis intercept sql:{};\r\nMapper方法是：{};\r\n 参数是：;", sql.getSql(), mapperMethod,args[1]);
        long start = System.currentTimeMillis();
        Object sqlResult = null;
        try
        {
            sqlResult = invocation.proceed();
            LOGGER.info(String.format("SQL 执行完毕，用时[%s]Ms",System.currentTimeMillis()-start));
        }catch (Throwable e)
        {
            LOGGER.error(String.format("Sql 执行异常 statement=[%s],execption=[%s]",e,e));
        }
        return sqlResult;
    }

    /**
     * @param target
     * @return
     */
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    /**
     * setProperties方法是用于在Mybatis配置文件中指定一些属性的
     * ，这个方法在Configuration初始化当前的Interceptor时就会执行，这里只是简单的取两个属性进行打印
     *
     * @param properties
     */
    @Override
    public void setProperties(Properties properties) {
        String name = properties.getProperty("name");
        String password = properties.getProperty("password");
        System.out.println(name + "<<name>>" + password);
    }
}
